package org.dwpd.service.user.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.ListUtil;
import jakarta.annotation.Resource;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.dwpd.domain.common.Code;
import org.dwpd.domain.common.Result;
import org.dwpd.domain.user.User;
import org.dwpd.domain.user.UserDTO;
import org.dwpd.domain.user.UserFans;
import org.dwpd.domain.user.UserInfo;
import org.dwpd.mapper.user.UserMapper;
import org.dwpd.service.user.UserService;
import org.dwpd.utils.JwtHelper;
import org.dwpd.utils.MD5Util;
import org.dwpd.utils.UserHolder;
import org.dwpd.utils.VerCodeGenerateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.dwpd.utils.RedisConstants.*;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    @Override
    public Result getUserDetailById() {
        // 判断用户是否登录
        UserDTO user = UserHolder.getUser();
        if (user == null) {
            return Result.unlogin(-2, "用户未登录");
        }
        Integer userId = user.getUserId();
        log.info("获取用户Id为：{} 的用户信息", userId);
        UserInfo userInfo = userMapper.selectUserDetailById(userId);
        if (userInfo == null) {
            return Result.fail(Code.RESULT_CODE_FAIL, "获取用户信息失败");
        }
        return Result.success(userInfo);
    }

    @Autowired
    public JavaMailSenderImpl javaMailSender;

    @Resource
    private JwtHelper jwtHelper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String em = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";

    public UserServiceImpl() {
    }

    /**
     * 注册账号
     * @param user
     * @return Result
     */
    public Result regist(User user, String code) {
        log.info("user:{};code:{}",user,code);
        //检查邮箱格式
        if (!user.getEmail().matches(em)) {
            //不正确，返回失败
            return Result.fail(-1,"邮箱格式错误!");
        }
        //获取缓存中的验证码
        String EmailCode = stringRedisTemplate.opsForValue().get(EMAIL_CODE_KEY + user.getEmail());
        //判断验证码是否过期
        if (EmailCode == null){
            return Result.fail(-1,"验证码过期");
        }
        //判断验证码是否正确
        if (!code.toUpperCase().equals(EmailCode)){
            //错误返回
            return Result.fail(-1,"验证码错误");
        }
        //查看用户是否存在
        User isHadUser = this.userMapper.queryByEmail(user.getEmail());
        if (isHadUser != null){
            //存在，返回错误信息
            return Result.fail(-1,"当前用户已存在");
        }
        //用户信息保存数据库
        user.setPassword(MD5Util.encrypt(user.getPassword()));
        this.userMapper.insertUser(user);
        // 创建用户等级信息
        userMapper.addUserLevel(user.getUserId());
        // 添加用户黑名单信息(正常状态)
        userMapper.addUserBlackList(user.getUserId());

        return Result.success("注册成功");
    }

    /**
     *  获取验证码
     * @param email
     * @return Result
     */
    public Result getMailCode(String email) {
        log.info("email:{}",email);
        //校验邮箱格式
        if (!email.matches(em)) {
            //不正确，返回失败
            return Result.fail(-1,"邮箱格式错误!");
        }
        MimeMessage mimeMessage = this.javaMailSender.createMimeMessage();
        HashMap<String, String> map = new HashMap();
        //检查email格式是否正确
        try {
            //生成验证码
            String verCode = VerCodeGenerateUtil.getVerCode();
            //存储到redis中并设置1分钟过期时间
            stringRedisTemplate.opsForValue().set(EMAIL_CODE_KEY+email,verCode,EMAIL_CODE_TTL,TimeUnit.MINUTES);
            //生成邮件
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
            mimeMessageHelper.setFrom(new InternetAddress(this.javaMailSender.getUsername(), "乐拼一夏"));
            mimeMessageHelper.setTo(email);
            mimeMessageHelper.setSubject("【乐拼一夏】邮箱注册");
            mimeMessageHelper.setText("您的验证码为:" + verCode + ",有效期为3分钟!");
            //发送邮件
            this.javaMailSender.send(mimeMessage);
            log.info("mimeMessage:{}",mimeMessage);
        } catch (Exception var7) {
            var7.printStackTrace();
            return Result.fail(-1,"服务繁忙，请稍后再试");
        }

        return Result.success("");
    }

    public Result login(User user, HttpServletRequest request) {
        //校验邮箱格式
        if (!user.getEmail().matches(em)){
            return Result.fail(-1,"邮箱格式错误");
        }
        log.info("pre: {}", user);
        UserInfo userInfo = this.userMapper.queryByEmailAndPassword(user.getEmail(), MD5Util.encrypt(user.getPassword()));

        log.info("userInfo: {}", userInfo);
        if (userInfo == null) {
            return Result.fail(-1, "账号或密码错误");
        } else {
            String token = jwtHelper.createToken();
            HashMap<String, String> map = new HashMap();
            map.put("token", token);
            // 将用户基本信息存入缓存中
            log.info("token:{}", token);
            String key = LOGIN_USER_KEY + token;
            // 若头像未设置，则设置未默认值
            if (userInfo.getUserImage() == null) {
                userInfo.setUserImage(" ");
            }
            Map<String, Object> userMap = BeanUtil.beanToMap(userInfo, new HashMap<>(),
                    CopyOptions.create().setIgnoreNullValue(true)
                            .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
            stringRedisTemplate.opsForHash().putAll(key, userMap);
            // 设置token有效期
            stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);
            // 将token存入session，用户后续调用WebSocket时调用
            HttpSession session = request.getSession(true); // 为true代表创建会话
            log.info("创建session：{}", session);
            session.setAttribute("token", token);
            // 返回token给前端
            return Result.success(map);
        }
    }

    public Result modifyPassword(User user,String emailCode) {
        log.info("user:{};code:{}",user,emailCode);
        //检查邮箱格式
        if (!user.getEmail().matches(em)) {
            //不正确，返回失败
            return Result.fail(-1,"邮箱格式错误!");
        }
        //获取缓存中的验证码
        String EmailCode = stringRedisTemplate.opsForValue().get(EMAIL_CODE_KEY + user.getEmail());
        //判断验证码是否过期
        if (EmailCode == null){
            return Result.fail(-1,"验证码过期");
        }
        //判断验证码是否正确
        if (!emailCode.toUpperCase().equals(EmailCode)){
            //错误返回
            return Result.fail(-1,"验证码错误");
        }
        //查看用户是否存在
        User isHadUser = this.userMapper.queryByEmail(user.getEmail());
        if (isHadUser == null) {
            return Result.fail(-1, "该用户不存在");
        } else {
            int row = this.userMapper.updatePasswordByEmail(user.getEmail(), MD5Util.encrypt(user.getPassword()));
            return row > 0 ? Result.success("修改成功") : Result.fail(-1, "修改失败");
        }
    }

    @Override
    public Result updateImage(String userImage, HttpServletRequest request) {

        // 判断用户是否登录
        UserDTO user = UserHolder.getUser();
        if (user == null) {
            return Result.unlogin(-2, "用户未登录");
        }
        Integer userId = user.getUserId();
        log.info("更换用户 {} 头像: {}", userId, userImage);

        // 修改用户个人信息头像
        userMapper.updateUserImage(userId, userImage);

        // 修改留言板中用户头像
        userMapper.updateBoardUserImage(userId, userImage);

        // 修改缓存中用户头像
        String token = request.getHeader("token");
        if (token == null) {
            return Result.fail(-2, "用户未登录");
        }
        stringRedisTemplate.opsForHash().put(LOGIN_USER_KEY + token, "userImage", userImage);

        return Result.success("修改用户头像成功");
    }

    @Override
    public Result updateUserInfo(UserInfo userInfo, HttpServletRequest request) {

        // 1. 判断用户是否登录
        UserDTO user = UserHolder.getUser();
        if (user == null) {
            return Result.unlogin(-2, "用户未登录");
        }
        Integer userId = user.getUserId();
        userInfo.setUserId(userId);
        log.info("修改用户信息, UserInfo: {}", userInfo);

        // 2. 获取token用于调用缓存
        String token = request.getHeader("token");
        if (token == null) {
            return Result.fail(-2, "用户未登录");
        }

        // 3. 修改缓存中的用户信息
        String key = LOGIN_USER_KEY + token;
        updateRedisUser(key, userInfo);

        // 4. 修改数据库中的用户信息
        Integer total = userMapper.updateUserInfo(userInfo);
        if (total == null || total <= 0) {
            return Result.fail(-1, "修改用户信息失败");
        }

        // 5. 返回结果
        return Result.success("修改用户信息成功");
    }

    @Override
    public Result queryOnSeatUsers(Integer messageId) {
        Set<String> members = stringRedisTemplate.opsForSet().members(LOCK_MESSAGE_KEY + messageId);
        List<Integer> userIds = members.stream().map(Integer::valueOf).collect(Collectors.toList());
        List<UserDTO> userDTOS = userMapper.selectUserByIds(userIds);
        if (userDTOS == null || userDTOS.isEmpty()) {
            return Result.success(ListUtil.empty());
        }
        return Result.success(userDTOS);
    }

    @Override
    public Result getUserDetailByUserId(Integer userId) {
        // 1. 判断用户是否登录
        UserDTO user = UserHolder.getUser();
        if (user == null) {
            return Result.unlogin(-2, "用户未登录");
        }
        // 2. 查询数据库返回用户数据
        UserInfo userInfo = userMapper.selectUserDetailById(userId);
        if (userInfo == null) {
            return Result.fail(Code.RESULT_CODE_FAIL, "获取用户信息失败");
        }
        return Result.success(userInfo);
    }

    @Override
    public void updateUserLevel() {

        log.info("定时修改用户等级");
        userMapper.updateUserLevel();

        // 修改用户信誉分(标准：原始分 + (完成帖 * 5 - 超时帖 * 10))
        userMapper.updateUserGradeByTask();

    }

    private void updateRedisUser(String key, UserInfo userInfo) {

        if (userInfo.getUserName() != null) {
            stringRedisTemplate.opsForHash().put(key, "userName", userInfo.getUserName());
        }
        if (userInfo.getSex() != null) {
            stringRedisTemplate.opsForHash().put(key, "sex", userInfo.getSex().toString());
        }
        if (userInfo.getPhone() != null) {
            stringRedisTemplate.opsForHash().put(key, "phone", userInfo.getPhone());
        }
        if (userInfo.getIntroduce() != null) {
            stringRedisTemplate.opsForHash().put(key, "introduce", userInfo.getIntroduce());
        }
    }

    @Override
    public List<UserFans> selectFansByIds(List<Integer> ids) {
        return userMapper.selectFansByIds(ids);
    }
}
